package g

import (
	"math"
	"net/rpc"
	"sync"
	"time"

	"github.com/toolkits/net"
)

// SingleConnRPCClient struct
type SingleConnRPCClient struct {
	sync.Mutex
	rpcClient *rpc.Client
	RPCServer string
	Timeout   time.Duration
}

// 关闭RPC请求
func (c *SingleConnRPCClient) close() {
	if c.rpcClient != nil {
		c.rpcClient.Close()
		c.rpcClient = nil
	}
}

// insureConn 判断RPC请求是否断开，保持重连状态
func (c *SingleConnRPCClient) insureConn() {
	if c.rpcClient != nil {
		return
	}

	var err error
	retry := 1

	for {
		if c.rpcClient != nil {
			return
		}

		c.rpcClient, err = net.JsonRpcClient("tcp", c.RPCServer, c.Timeout)
		if err == nil {
			return
		}

		Logger.Error("dial %s fail: %v", c.RPCServer, err)

		if retry > 6 {
			retry = 1
		}

		time.Sleep(time.Duration(math.Pow(2.0, float64(retry))) * time.Second)
		retry++
	}
}

// Call 发起RPC请求
func (c *SingleConnRPCClient) Call(method string, args interface{}, reply interface{}) error {
	c.Lock()
	defer func() {
		c.Unlock()
	}()

	c.insureConn()

	timeout := time.Duration(50 * time.Second)
	done := make(chan error)

	go func() {
		err := c.rpcClient.Call(method, args, reply)
		done <- err
	}()

	select {
	case <-time.After(timeout):
		Logger.Info("[WARN] rpc call timeout %v => %v", c.rpcClient, c.RPCServer)
	case err := <-done:
		if err != nil {
			return err
		}
	}

	return nil
}
